//
//  QNCategory.m
//  QNCentralModule_iOS
//
//  Created by donyau on 2018/11/5.
//  Copyright © 2018 Yolanda. All rights reserved.
//

#import "QNCategory.h"
#import <objc/runtime.h>

@implementation NSTimer (QNTimer)
+ (void)qnCentralExecBlock:(NSTimer *)timer {
    if ([timer userInfo]) {
        void (^block)(NSTimer *timer) = (void (^)(NSTimer *timer))[timer userInfo];
        block(timer);
    }
}

+ (NSTimer *)qnCentralScheduledTimerWithTimeInterval:(NSTimeInterval)seconds block:(void (^)(NSTimer *timer))block repeats:(BOOL)repeats {
    return [NSTimer scheduledTimerWithTimeInterval:seconds target:self selector:@selector(qnCentralExecBlock:) userInfo:[block copy] repeats:repeats];
}
@end


static const int QNCentralBlockKey;
@interface QNCentralNSObjectKVOBlockTarget : NSObject
@property (nonatomic, copy) void (^block)(__weak id obj, id oldVal, id newVal);
- (id)initWithBlock:(void (^)(__weak id obj, id oldVal, id newVal))block;
@end

@implementation QNCentralNSObjectKVOBlockTarget

- (id)initWithBlock:(void (^)(__weak id obj, id oldVal, id newVal))block {
    self = [super init];
    if (self) {
        self.block = block;
    }
    return self;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (!self.block) return;
    
    BOOL isPrior = [[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue];
    if (isPrior) return;
    
    NSKeyValueChange changeKind = [[change objectForKey:NSKeyValueChangeKindKey] integerValue];
    if (changeKind != NSKeyValueChangeSetting) return;
    
    id oldVal = [change objectForKey:NSKeyValueChangeOldKey];
    if (oldVal == [NSNull null]) oldVal = nil;
    
    id newVal = [change objectForKey:NSKeyValueChangeNewKey];
    if (newVal == [NSNull null]) newVal = nil;
    
    self.block(object, oldVal, newVal);
}
@end


@implementation NSObject (QNKVO)
- (void)qnAddObserverBlockForKeyPath:(NSString *)keyPath block:(void (^)(id _Nonnull, id _Nullable, id _Nullable))block {
    if (!keyPath || !block) return;
    QNCentralNSObjectKVOBlockTarget *target = [[QNCentralNSObjectKVOBlockTarget alloc] initWithBlock:block];
    NSMutableDictionary *dic = [self qnCentralAllNSObjectObserverBlocks];
    NSMutableArray *arr = dic[keyPath];
    if (!arr) {
        arr = [NSMutableArray new];
        dic[keyPath] = arr;
    }
    [arr addObject:target];
    [self addObserver:target forKeyPath:keyPath options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
}

- (void)qnRemoveObserverBlocksForKeyPath:(NSString *)keyPath {
    if (!keyPath) return;
    NSMutableDictionary *dic = [self qnCentralAllNSObjectObserverBlocks];
    NSMutableArray *arr = dic[keyPath];
    [arr enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
        [self removeObserver:obj forKeyPath:keyPath];
    }];
    
    [dic removeObjectForKey:keyPath];
}

- (NSMutableDictionary *)qnCentralAllNSObjectObserverBlocks {
    NSMutableDictionary *targets =  getAssociatedObject(QNCentralBlockKey)
    if (!targets) {
        targets = [NSMutableDictionary new];
        setAssociatedObject(QNCentralBlockKey, targets)
    }
    return targets;
}

@end


